#ifndef __BLOCK_QUEUE_HPP__
#define __BLOCK_QUEUE_HPP__

#include <iostream>
#include <string>
#include <queue>
#include <pthread.h>

template <typename T>
class BlockQueue
{
private:
  std::queue<T> _block_queue;   // 阻塞队列
  int _cap;                     // 总上限
  pthread_mutex_t _mutex;       // 保护_block_queue的互斥量
  pthread_cond_t _product_cond; // 专门给生产者提供的条件变量
  pthread_cond_t _consume_cond; // 专门给消费者提供的条件变量
  int _productor_wait_num;      // 生产者等待数
  int _consumer_wait_num;       // 消费者等待数

public:
  BlockQueue(int cap)
      : _cap(cap)
  {
    pthread_mutex_init(&_mutex, nullptr);
    pthread_cond_init(&_product_cond, nullptr);
    pthread_cond_init(&_consume_cond, nullptr);
  }

  bool isEmpty()
  {
    return _block_queue.empty();
  }

  bool isFull()
  {
    return _block_queue.size() == _cap;
  }

  void Enqueue(const T &in) // 生产者用的接口
  {
    pthread_mutex_lock(&_mutex);
    while (isFull()) // 必须while重新判断，不能用if，防止伪唤醒
    {
      _productor_wait_num++;
      pthread_cond_wait(&_product_cond, &_mutex); // 当条件满足，线程唤醒，pthread_cond_wait要求线程必须重新竞争mutex锁，谁竞争成功谁成功返回，否则继续等待
      _productor_wait_num--;
    }
    // 进行生产
    _block_queue.push(std::move(in));
    // 通知消费者可以消费了
    if (_consumer_wait_num > 0) // 如果有等待的消费者直接唤醒，反之解锁就行了
      pthread_cond_signal(&_consume_cond);
    pthread_mutex_unlock(&_mutex);
  }

  void Pop(T *out) // 消费者用的接口
  {
    pthread_mutex_lock(&_mutex);
    while (isEmpty()) // 必须while重新判断，不能用if，防止伪唤醒
    {
      _consumer_wait_num++;
      pthread_cond_wait(&_consume_cond, &_mutex);
      _consumer_wait_num--;
    }
    // 进行消费
    *out = _block_queue.front();
    _block_queue.pop();
    // 有位置了，通知生产者可以生产了
    if (_productor_wait_num > 0) // 如果有等待的生产者直接唤醒，反之解锁就行了
      pthread_cond_signal(&_product_cond);
    pthread_mutex_unlock(&_mutex);
  }

  ~BlockQueue()
  {
    pthread_mutex_destroy(&_mutex);
    pthread_cond_destroy(&_product_cond);
    pthread_cond_destroy(&_consume_cond);
  }
};

#endif